home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
readers
/
nn-tk.001
/
nn-tk~
/
nn
/
nntp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-03-11
|
29KB
|
1,224 lines
/*
* nntp module for nn.
*
* The original taken from the nntp 1.5 clientlib.c
* Modified heavily for nn.
*
* Rene' Seindal (seindal@diku.dk) Thu Dec 1 18:41:23 1988
*
* I have modified Rene's code quite a lot for 6.4 -- I hope he
* can still recognize a bit here and a byte there; in any case,
* any mistakes are mine :-) ++Kim
*/
#include "config.h"
/*
* nn maintains a cache of recently used articles to improve efficiency.
* To change the size of the cache, define NNTPCACHE in config.h to be
* the new size of this cache.
*/
#ifndef NNTPCACHE
#define NNTPCACHE 10
#endif
#ifdef NNTP
#include <stdio.h>
#include "nntp.h"
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include "hash.h"
#include "hdbm.h"
#include "newsoverview.h"
/* This is necessary due to the definitions in m-XXX.h */
#include <netinet/in.h>
/* nntp.c */
static void debug_msg __APROTO((char *prefix, char *str));
static void io_error __APROTO((void));
static int find_server __APROTO((void));
static get_server_line __APROTO((char *string, int size));
static get_server __APROTO((char *string, int size));
static get_socket __APROTO((void));
static connect_server __APROTO((void));
static put_server __APROTO((char *string));
static ask_server __APROTO(());
static copy_text __APROTO((register FILE *fp, char *name));
static do_set_group __APROTO((int fst));
static struct cache *search_cache __APROTO((article_number art, group_header *gh));
static struct cache *new_cache_slot __APROTO((void));
static void clean_cache __APROTO((void));
static sort_art_list __APROTO((register article_number *f1, register article_number *f2));
import char *db_directory, *tmp_directory, *news_active;
import char *server_nntp;
export char nntp_server[256] = ""; /* name of nntp server */
export int nntp_failed = 0; /* bool: t iff connection is broken in
nntp_get_article() or nntp_get_active() */
export int nntp_cache_size = NNTPCACHE;
export char *nntp_cache_dir = NULL;
export int nntp_local_server = 0;
export int nntp_debug = 0;
export int nntp_readahead = 40;
import int silent, no_update;
import int nntp_progress_a;
import int nntp_abt;
import int newsrc_only;
import int group_list_unsub;
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
import int sys_nerr;
import char *sys_errlist[];
#endif
extern void nn_exitmsg();
extern void sys_error();
extern int sys_warning();
#if defined(__FreeBSD__) || defined(__NetBSD__)
#define syserr() strerror(errno)
#else
#define syserr() (errno >= 0 && errno < sys_nerr ? \
sys_errlist[errno] : "Unknown error.")
#endif
import char *mktemp();
static FILE *nntp_in = NULL; /* fp for reading from server */
static FILE *nntp_out = NULL; /* fp for writing to server */
static int is_connected = 0; /* bool: t iff we are connected */
static group_header *group_hd; /* ptr to servers current group */
static int group_is_set = 0; /* bool: t iff group_hd is set */
static int try_again = 0; /* bool: t if timeout forces retry */
static int can_post = 0; /* bool: t iff NNTP server accepts postings */
#define ERR_TIMEOUT 503 /* Response code for timeout */
/* Same value as ERR_FAULT */
#ifdef NO_RENAME
static rename(old, new)
char *old, *new;
{
if (unlink(new) < 0 && errno != ENOENT) return -1;
if (link(old, new) < 0) return -1;
return unlink(old);
}
#endif /* NO_RENAME */
/*
* debug_msg: print a debug message.
*
* The master appends prefix and str to a log file, and clients
* prints it as a message.
*
* This is controlled via the nntp-debug variable in nn, and
* the option -D2 (or -D3 if the normal -D option should also
* be turned on). Debug output from the master is written in
* $TMP/nnmaster.log.
*/
static void
debug_msg(prefix, str)
char *prefix, *str;
{
#ifdef TK
printf("NNTP%s %s\n", prefix, str);
#else /*TK*/
msg("NNTP%s %s", prefix, str);
user_delay(1);
#endif /*TK*/
}
/*
* io_error: signal an I/O error in talking to the server.
*
* An nn client terminates a session with the user. The master
* simply closes the connection. The flag nntp_failed is set, for
* use by the master to terminate collection.
*
* BUG: if the nntp server is forcibly killed, errno can contain a
* bogus value, resulting in strange error messages. It is
* probably better just to write out the numerical value of errno.
*/
static void
io_error()
{
nn_exitmsg(1, "Lost connection to NNTP server %s: %s", nntp_server, syserr());
/* NOTREACHED */
}
/*
* find_server: Find out which host to use as NNTP server.
*
* This is done by consulting the file NNTP_SERVER (defined in
* config.h). Set nntp_server[] to the host's name.
*/
static int
find_server()
{
char *cp, *name;
char buf[BUFSIZ];
FILE *fp;
/*
* This feature cannot normally be enabled, because the database and
* the users rc file contains references to articles by number, and
* these numbers are not unique across NNTP servers.
*/
/*
* But, since we no longer have a database to worry about, give the user
* the rope. If he wants to hang himself, then let him! :-)
* Let the user worry about keeping his .newsrc straight.
*/
if ((cp = getenv("NNTPSERVER")) != NULL) {
strncpy(nntp_server, cp, sizeof nntp_server);
return 1;
}
name = NNTP_SERVER;
if (*name != '/')
name = relative(lib_directory, name);
if ((fp = open_file(name, OPEN_READ)) != NULL) {
while (fgets(buf, sizeof buf, fp) != 0) {
if (*buf == '#' || *buf == '\n')
continue;
if ((cp = strchr(buf, '\n')) != 0)
*cp = '\0';
strncpy(nntp_server, buf, sizeof nntp_server);
fclose(fp);
return 1;
}
fclose(fp);
}
return 0;
}
/*
* get_server_line: get a line from the server.
*
* Expects to be connected to the server.
* The line can be any kind of line, i.e., either response or text.
* Returns length of line if no error.
* If error and master, then return -1, else terminate.
*/
static int
get_server_line(string, size)
register char *string;
register int size;
{
if (fgets(string, size, nntp_in) == NULL) {
io_error();
return -1;
}
size = strlen(string);
if (size < 2 || !(string[size-2] == '\r' && string[size-1] == '\n'))
return size; /* XXX */
string[size-2] = '\0'; /* nuke CRLF */
return size-2;
}
/*
* get_server: get a response line from the server.
*
* Expects to be connected to the server.
* Returns the numerical value of the reponse, or -1 in case of errors.
*/
static int
get_server(string, size)
char *string;
int size;
{
if (get_server_line(string, size) < 0)
return -1;
if (nntp_debug) debug_msg("<<<", string);
return isdigit(*string) ? atoi(string) : 0;
}
/*
* get_socket: get a connection to the nntp server.
*
* Errors can happen when YP services or DNS are temporarily down or
* hung, so we log errors and return failure rather than exitting if we
* are the master. The effects of retrying every 15 minutes (or whatever
* the -r interval is) are not that bad. Dave Olson, SGI
*/
static int
get_socket()
{
int s;
struct sockaddr_in sin;
struct servent *getservbyname(), *sp;
struct hostent *gethostbyname(), *hp;
#ifdef h_addr
int x = 0;
register char **cp;
#endif /* h_addr */
if ((sp = getservbyname("nntp", "tcp")) == NULL)
return sys_warning("nntp/tcp: Unknown service.\n");
s = 2;
while ((hp = gethostbyname(nntp_server)) == NULL) {
if (--s < 0) goto host_err;
sleep(10);
}
clearobj(&sin, sin, 1);
sin.sin_family = hp->h_addrtype;
sin.sin_port = sp->s_port;
#ifdef h_addr
/* get a socket and initiate connection -- use multiple addresses */
s = x = -1;
for (cp = hp->h_addr_list; cp && *cp; cp++) {
s = socket(hp->h_addrtype, SOCK_STREAM, 0);
if (s < 0) goto sock_err;
#ifdef NO_MEMCPY
bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
#else /* NO_MEMCPY */
memcpy((char *)&sin.sin_addr, *cp, hp->h_length);
#endif /* NO_MEMCPY */
/* Quick hack to work around interrupting system calls.. */
while((x = connect(s, (struct sockaddr *)&sin, sizeof (sin))) < 0 &&
errno == EINTR) sleep(1);
if (x == 0)
break;
msg("Connection to %s failed: %s", nntp_server, syserr());
(void) close(s);
s = -1;
}
if (x < 0)
sys_warning("Giving up on NNTP server %s!", nntp_server);
#else /* h_addr */ /* no name server */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
goto sock_err;
/* And then connect */
#ifdef NO_MEMCPY
bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
#else /* NO_MEMCPY */
memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
#endif /* NO_MEMCPY */
if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0)
goto conn_err;
#endif /* h_addr */
return s;
host_err:
sys_warning("NNTP server %s unknown.\n", nntp_server);
return -1;
sock_err:
sys_warning("Can't get NNTP socket: %s", syserr());
return -1;
conn_err:
(void) close(s);
sys_warning("Connection to %s failed: %s", nntp_server, syserr());
return -1;
}
/*
* connect_server: initialise a connection to the nntp server.
*
* It expects nntp_server[] to be set previously, by a call to
* nntp_check. It is called from nntp_get_article() and
* nntp_get_active() if there is no established connection.
*/
static int
connect_server()
{
int sockt_rd, sockt_wr;
int response;
char line[NNTP_STRLEN];
if (!silent)
msg("Connecting to NNTP server %s ...", nntp_server);
nntp_failed = 1;
is_connected = 0;
sockt_rd = get_socket();
if (sockt_rd < 0)
return -1;
if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
close(sockt_rd);
return -1;
}
sockt_wr = dup(sockt_rd);
if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
close(sockt_wr);
fclose(nntp_in);
nntp_in = NULL; /* from above */
return -1;
}
/* Now get the server's signon message */
response = get_server(line, sizeof(line));
switch (response) {
case OK_CANPOST:
can_post = 1;
break;
case OK_NOPOST:
can_post = 0;
break;
default:
nn_exitmsg(1, line);
/* NOTREACHED */
}
/* Try and speak NNRP style reader protocol */
sprintf(line, "MODE READER");
if (put_server(line) >= 0) {
/* See what we got if from a NNRP compliant server like INN's nnrpd */
response = get_server(line, sizeof(line));
switch (response) {
case OK_CANPOST:
can_post = 1;
break;
case OK_NOPOST:
can_post = 0;
break;
case ERR_COMMAND:
default:
/* if it doesn't understand MODE READER, we don't care.. :-) */
break;
}
}
if (!silent)
msg("Connection to NNTP server %s ... ok (%s)",
nntp_server, can_post ? "posting is allowed" : "no posting");
is_connected = 1;
group_is_set = 0;
nntp_failed = 0;
try_again = 0;
#ifdef NNTP
return send_user_info();
#else
return 0;
#endif
}
/*
* put_server: send a line to the nntp server.
*
* Expects to be connected to the server.
*/
static int
put_server(string)
char *string;
{
if (nntp_debug) debug_msg(">>>", string);
fprintf(nntp_out, "%s\r\n", string);
if (fflush(nntp_out) == EOF) {
io_error();
return -1;
}
return 0;
}
/* kill transfer by disconnecting and reconnecting to the server */
void
nntp_restart_server()
{
(void) fclose(nntp_out);
(void) fclose(nntp_in);
is_connected = 0;
connect_server();
}
/*
* ask_server: ask the server a question and return the answer.
*
* Expects to be connected to the server.
* Returns the numerical value of the reponse, or -1 in case of
* errors.
* If the first paramter is non-null returns the response in
* it (has to point at a big enough buffer).
* Contains some code to handle server timeouts intelligently.
*/
/*
* LIST XXX returns fatal ERR_FAULT code if requested list does not exist
* This is only fatal for LIST ACTIVE -- else change to ERR_NOGROUPS
* (Note that some nntp servers spew a syntax error on LIST ACTIVE.TIMES
* we change it to ERR_NOGROUPS also)
*/
static int fix_list_response = 0;
/*VARARGS*/
static int
ask_server(va_alist)
va_dcl
{
char buf[NNTP_STRLEN];
char *fmt, *obuf;
int response;
int fix_err;
use_vararg;
fix_err = fix_list_response;
fix_list_response = 0;
start_vararg;
obuf = va_arg1(char *);
fmt = va_arg2(char *);
vsprintf(buf, fmt, va_args3toN);
end_vararg;
if (put_server(buf) < 0)
return -1;
response = get_server(buf, sizeof(buf));
if (obuf) {
strcpy(obuf,buf);
}
/*
* Handle the response from the server. Responses are handled as
* followes:
*
* 100-199 Informational. Passed back. (should they be ignored?).
* 200-299 Ok messages. Passed back.
* 300-399 Ok and proceed. Can not happen in nn.
* 400-499 Errors (no article, etc). Passed up and handled there.
* 500-599 Fatal NNTP errors. Handled below.
*/
if (response == ERR_GOODBYE || response > ERR_COMMAND) {
if (fix_err && (response == ERR_FAULT || response == ERR_CMDSYN))
return ERR_NOGROUP;
nntp_failed = 1;
nntp_close_server();
if (response != ERR_TIMEOUT && response != ERR_GOODBYE) {
/* if not timeout and not goodbye, complain */
sys_error("NNTP %s response: %d", buf, response);
/* NOTREACHED */
}
if (response == ERR_GOODBYE) {
sys_warning("NNTP %s response: %d", buf, response);
}
try_again = 1;
group_is_set = 0;
}
return response;
}
/*
* copy_text: copy text response into file.
*
* Copies a text response into an open file.
* Return -1 on error, 0 otherwise. It is treated as an error, if
* the returned response it not what was expected.
*/
static int last_copy_blank;
static int
copy_text(fp, name)
register FILE *fp;
char *name;
{
char buf[NNTP_STRLEN];
register char *cp;
register int nlines;
nlines = 0;
last_copy_blank = 0;
nntp_abt = 0;
while (get_server_line(buf, sizeof buf) >= 0) {
cp = buf;
if (*cp == '.')
if (*++cp == '\0') {
if (nlines <= 0) break;
if (nntp_debug) {
sprintf(buf, "%d lines", nlines);
debug_msg("COPY", buf);
}
tk("nntp_kmsg");
return 0;
}
last_copy_blank = (*cp == NUL);
fputs(cp, fp);
putc('\n', fp);
nlines++;
if (nntp_progress_a && nlines > nntp_progress_a &&
nlines % nntp_progress_a == 0) {
tk("nntp_lmsg {Reading %s:%5d} 1",name, nlines);
}
if (nntp_abt) {
nntp_restart_server();
if (nntp_debug) {
sprintf(buf, "%d lines", nlines);
debug_msg("ABORT", buf);
}
tk("nntp_kmsg");
return 0;
}
}
fclose(fp);
tk("nntp_kmsg");
if (nntp_debug) debug_msg("COPY", "EMPTY");
return -1;
}
static void
info_group(gh,first,last)
group_header *gh;
int first, last;
{
gh->group_flag |= G_RDAHEAD;
if (last > 0) {
gh->first_a_article = first;
gh->last_a_article = last;
gh->first_db_article = first;
gh->last_db_article = last;
if (gh->last_article < first) {
gh->last_article = first - 1;
}
gh->group_flag &= ~G_UNKNOWN;
add_unread(gh, 1);
}
if (!(gh->group_flag & G_UNSUBSCRIBED) || group_list_unsub)
tk_group_ent(gh,"list_update");
}
static int
do_set_group(fst)
int fst;
{
int n, i;
int r, l, first, last;
int response;
char buf[NNTP_STRLEN],gn[NNTP_STRLEN];
group_header *gh;
n = 0;
if (newsrc_only &&
(!(group_hd->group_flag & G_RDAHEAD)
|| (group_hd->next_group
&& !(group_hd->next_group->group_flag & G_RDAHEAD)))) {
for (gh = group_hd; gh; gh = gh->next_group) {
sprintf(buf,"GROUP %s",gh->group_name);
if (put_server(buf) < 0)
printf("ERR GROUP %s\n",gh->group_name);
n++;
if (n > nntp_readahead)
break;
}
for (i = 0; i <= n - 1; i++) {
response = get_server(buf, sizeof(buf));
sscanf(buf, "%d %d %d %d %s", &r, &l, &first, &last, gn);
if (buf[0] == '2') {
gh = lookup(gn);
info_group(gh,first,last);
}
}
}
/* don't get group info when empty group for -A mode */
if (newsrc_only && (group_hd->group_flag & G_RDAHEAD)) {
if ((group_hd->unread_count == 0 && fst > group_hd->last_a_article)
|| group_hd->last_a_article == ART_MAX_DUMMY) {
return -1;
}
}
switch (n = ask_server(buf, "GROUP %s", group_hd->group_name)) {
case OK_GROUP:
group_is_set = 1;
sscanf(buf, "%d %d %d %d %s", &r, &l, &first, &last, gn);
info_group(group_hd,first,last);
return 1;
case ERR_NOGROUP:
log_entry('N', "NNTP: group %s not found", group_hd->group_name);
return -1;
default:
if (try_again) return 0; /* Handle nntp server timeouts */
break;
}
if (!nntp_failed) {
log_entry('N', "GROUP %s response: %d", group_hd->group_name, n);
nntp_failed = 1;
}
return -1;
}
/*
* The following functions implements a simple lru cache of recently
* accessed articles. It is a simple way to improve effeciency. Files
* must be kept by name, because the rest of the code expects to be able
* to open an article multiple times, and get separate file pointers.
*/
struct cache {
char *file_name; /* file name */
article_number art; /* article stored in file */
group_header *grp; /* from this group */
unsigned time; /* time last accessed */
} cache[NNTPCACHE];
static unsigned time_counter = 1; /* virtual time */
/*
* search_cache: search the cache for an (article, group) pair.
*
* Returns a pointer to the slot where it is, null otherwise
*/
static struct cache *search_cache(art, gh)
article_number art;
group_header *gh;
{
struct cache *cptr = cache;
int i;
if (nntp_cache_size > NNTPCACHE)
nntp_cache_size = NNTPCACHE;
for (i = 0; i < nntp_cache_size; i++, cptr++)
if (cptr->art == art && cptr->grp == gh) {
cptr->time = time_counter++;
return cptr;
}
return NULL;
}
/*
* new_cache_slot: get a free cache slot.
*
* Returns a pointer to the allocated slot.
* Frees the old filename, and allocates a new, unused filename.
* Cache files can also stored in a common directory defined in
* ~/.nn or CACHE_DIRECTORY if defined in config.h.
*/
static struct cache *new_cache_slot()
{
register struct cache *cptr = cache;
int i, lru = 0;
unsigned min_time = time_counter;
char name[FILENAME];
if (nntp_cache_dir == NULL) {
#ifdef CACHE_DIRECTORY
nntp_cache_dir = CACHE_DIRECTORY;
#else /* CACHE_DIRECTORY */
nntp_cache_dir = nn_directory;
#endif /* CACHE_DIRECTORY */
}
for (i = 0; i < nntp_cache_size; i++, cptr++)
if (min_time > cptr->time) {
min_time = cptr->time;
lru = i;
}
cptr = &cache[lru];
if (cptr->file_name == NULL) {
sprintf(name, "%s/nn-%d.%02d~", nntp_cache_dir, process_id, lru);
cptr->file_name = copy_str(name);
} else
unlink(cptr->file_name);
cptr->time = time_counter++;
return cptr;
}
/*
* clean_cache: clean up the cache.
*
* Removes all allocated files.
*/
static void clean_cache()
{
struct cache *cptr = cache;
int i;
for (i = 0; i < nntp_cache_size; i++, cptr++)
if (cptr->file_name)
unlink(cptr->file_name);
}
/*
* nntp_check: Find out whether we need to use NNTP.
*
* This is done by comparing the NNTP servers name with whatever
* nn_gethostname() returns.
* use_nntp and news_active are initialised as a side effect.
*/
void
nntp_check()
{
char host[128];
char *server_real_name;
struct hostent *tmp;
if (nntp_local_server) return;
if (server_nntp)
strncpy(nntp_server,server_nntp,256);
if (*nntp_server
#ifndef NO_NNTP_SERVER_READ
|| find_server()
#endif
) {
nn_gethostname(host, sizeof host);
tmp = gethostbyname(host);
if (tmp) {
strncpy(host, tmp->h_name, sizeof host);
}
tmp = gethostbyname(nntp_server);
if (tmp) {
server_real_name = tmp->h_name;
} else {
server_real_name = nntp_server;
}
use_nntp = (strcmp(host, server_real_name) != 0);
} else {
use_nntp = 0;
}
if (use_nntp) {
freeobj(news_active);
news_active = mk_file_name(nn_directory, "ACTIVE");
}
}
/*
* nntp_no_post: Check to see whether posting is allowed.
*/
int
nntp_no_post()
{
if (!is_connected && connect_server() < 0)
return 1; /* If we cannot connect, neither can inews */
if (can_post == 0) {
msg("NNTP server does not allow postings from this host. Sorry!");
return 1;
}
return 0;
}
/*
* nntp_set_group: set the server's current group.
*
* Actual communication is delayed until an article is accessed, to
* avoid unnecessary traffic.
*/
int
nntp_set_group(gh)
group_header *gh;
{
group_hd = gh;
group_is_set = 0;
return 0;
}
/*
* nntp_get_newsgroups: get a copy of the newsgroups file.
*
* Use the "LIST NEWSGROUPS" command to get the newsgroup descriptions.
* Based on code from: olson%anchor.esd@sgi.com (Dave Olson)
*/
FILE *nntp_get_newsgroups()
{
char *new_name;
FILE *new = NULL;
int n;
new_name = mktemp(relative(tmp_directory, ".nngrXXXXXX"));
new = open_file(new_name, OPEN_CREATE_RW|OPEN_UNLINK);
if (new == NULL) return NULL;
again:
if (!is_connected && connect_server() < 0) goto err;
fix_list_response = 1;
switch (n = ask_server(NULL, "LIST NEWSGROUPS")) {
case ERR_NOGROUP: /* really ERR_FAULT */
goto err;
case OK_GROUPS:
if (copy_text(new,"Group Names") == 0) {
if (fflush(new) != EOF) break;
fclose(new);
}
if (!nntp_failed) {
log_entry('N', "LIST NEWSGROUPS empty");
nntp_failed = 1;
}
return NULL;
default:
if (try_again) goto again; /* Handle nntp server timeouts */
log_entry('N', "LIST NEWSGROUPS response: %d", n);
goto err;
}
rewind(new);
return new;
err:
fclose(new);
return NULL;
}
/*
* nntp_get_article: get an article from the server.
*
* Returns a FILE pointer.
* If necessary the server's current group is set.
* The article (header and body) are copied into a file, so they
* are seekable (nn likes that).
*/
static char *mode_cmd[] = {
"ARTICLE",
"HEAD",
"BODY"
};
FILE *nntp_get_article(article, mode)
article_number article;
int mode; /* 0 => whole article, 1 => head only, 2 => body only */
{
FILE *tmp;
static struct cache *cptr;
int n;
again:
if (!is_connected && connect_server() < 0) {
return NULL;
}
/*
* Set the server group to the current group
*/
if (group_is_set == 0)
switch (do_set_group(0)) {
case -1:
return NULL;
case 0:
goto again;
case 1:
break;
}
/*
* Search the cache for the requested article, and allocate a new
* slot if necessary (if appending body, we already got it).
*/
if (mode != 2) {
cptr = search_cache(article, group_hd);
if (cptr != NULL) goto out;
cptr = new_cache_slot();
}
/*
* Copy the article.
*/
switch (n = ask_server(NULL, "%s %ld", mode_cmd[mode], (long)article)) {
case OK_ARTICLE:
case OK_HEAD:
tmp = open_file(cptr->file_name, OPEN_CREATE|MUST_EXIST);
if (copy_text(tmp,"Article ") < 0)
return NULL;
if (mode == 1 && !last_copy_blank)
fputc(NL, tmp); /* add blank line after header */
if (fclose(tmp) == EOF) goto err;
cptr->art = article;
cptr->grp = group_hd;
goto out;
case OK_BODY:
tmp = open_file(cptr->file_name, OPEN_APPEND|MUST_EXIST);
fseek(tmp, (off_t)0, 2);
if (copy_text(tmp,"Article") < 0)
return NULL;
if (fclose(tmp) == EOF) goto err;
goto out;
case ERR_NOARTIG:
/* Matt Heffron: ANUNEWS on VMS uses no such article error */
case ERR_NOART:
return NULL;
default:
if (try_again) goto again; /* Handle nntp server timeouts */
/* Matt Heffron: Which group? */
log_entry('N', "ARTICLE %ld response: %d (in Group %s)",
(long)article, n, group_hd->group_name);
nntp_failed = 1;
return NULL;
}
out:
return open_file(cptr->file_name, OPEN_READ|MUST_EXIST);
err:
sys_error('N', "Cannot write temporary file %s", cptr->file_name);
return NULL; /* ode to the fussy compiler */
}
/*
* Return local file name holding article
*/
char *nntp_get_filename(art, gh)
article_number art;
group_header *gh;
{
struct cache *cptr;
cptr = search_cache(art, gh);
return cptr == NULL ? NULL : cptr->file_name;
}
/*
* nntp_close_server: close the connection to the server.
*/
void
nntp_close_server()
{
if (!is_connected)
return;
if (!nntp_failed) { /* avoid infinite recursion */
int n;
n = ask_server(NULL, "QUIT");
if (n != OK_GOODBYE)
; /* WHAT NOW ??? */
}
(void) fclose(nntp_out);
(void) fclose(nntp_in);
is_connected = 0;
}
/*
* nntp_cleanup: clean up after an nntp session.
*
* Called from nn_exit().
*/
void
nntp_cleanup()
{
if (is_connected)
nntp_close_server();
clean_cache();
}
/*************************************************************/
/*
** Prime the nntp server to snarf the overview file for a newsgroup.
** Sends the XOVER command and prepares to read the result.
*/
struct novgroup *
nntp_get_overview(gh, first, last)
group_header *gh;
article_number first, last;
{
int n;
again:
nntp_set_group(gh);
switch (do_set_group(first)) {
case -1:
return NULL;
case 0:
goto again;
}
n = ask_server(NULL, "XOVER %d-%d", first, last);
switch (n) {
case OK_NOV:
return novstream(nntp_in);
default:
if (try_again)
goto again; /* Handle nntp server timeouts */
log_entry('N', "XOVER response: %d", n);
return NULL;
}
}
/*
* nntp_fopen_list(cmd): Send some variant of a LIST command to the
* NNTP server. returns NULL if the file to be LISTed doesn't exist
* on the server, else returns the nntp_in FILE descriptor, thus
* simulating fopen().
* nntp_fgets() is later used to read a line from the nntp_in FILE.
*/
FILE *
nntp_fopen_list(cmd)
char *cmd;
{
int n;
again:
if (!is_connected && connect_server() < 0)
return NULL;
fix_list_response = 1;
switch (n = ask_server(NULL, cmd)) {
case ERR_NOGROUP: /* really ERR_FAULT - no such file on server */
return NULL;
case OK_GROUPS: /* aka NNTP_LIST_FOLLOWS_VAL */
return nntp_in;
default:
if (try_again) goto again; /* Handle nntp server timeouts */
log_entry('N', "`%s' response: %d", cmd, n);
return NULL;
}
}
FILE *
nntp_fopen()
{
int n;
if (!is_connected && connect_server() < 0)
return NULL;
else
return nntp_in;
}
/*
* nntp_fgets() - Get a line from a file stored on the NNTP server.
* Strips any hidden "." at beginning of line and returns a pointer to
* the line. line will be terminated by NL NUL.
* Returns NULL when NNTP sends the terminating "." to indicate EOF.
*/
char *
nntp_fgets(buf, bufsize)
char *buf;
int bufsize;
{
char *cp;
register int size;
if ((size = get_server_line(buf, bufsize - 1)) < 0)
return NULL; /* Can't happen with NOV (we'd rather die first) */
cp = buf;
if (*cp == '.') {
if (*++cp == '\0')
return NULL;
}
cp[size] = '\n';
cp[size+1] = '\0';
return cp;
}
int send_user_info()
{
int n;
char auth_path[255];
char auth_user[50],auth_pass[50];
FILE *fp;
fp=fopen(relative(nn_directory, "authinfo"),"r");
if (fp!=NULL) {
int i,j;
n=0;i=0;
while((i=fgetc(fp))!=EOF) {
if (i==' ') {
auth_user[n]='\0';
break;
}
auth_user[n++]=(char)i;
}
auth_user[n]='\0';
n=0;
while((i=fgetc(fp))!=EOF) {
if (i=='\n') {
auth_pass[n]='\0';
break;
}
auth_pass[n++]=(char)i;
}
fclose(fp);
ask_server(NULL, "authinfo user %s", auth_user);
n = ask_server(NULL, "authinfo pass %s", auth_pass);
if (n== 281) { /* OK */
msg(" Authentication to server succesfull ");
}
else {
msg (" Authentication to server unsuccesfull ");
return -1;
}
}
return 0;
}
#endif /* NNTP */